//go:build windows
// +build windows

package 浏览器类

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/lxn/win"
	"log"
	"os"
	"reflect"
	"strconv"
	"sync"
	"unsafe"

	"gitee.com/go_888/webview2/internal/edge"
	"gitee.com/go_888/webview2/internal/w32"
	"golang.org/x/sys/windows"
)

var webview2路径 string

type Web视图 struct {
	窗口句柄   uintptr
	浏览器    接口_浏览器
	是否自动聚焦 bool
	最大尺寸   w32.X结构_坐标
	最小尺寸   w32.X结构_坐标
	锁      sync.Mutex
	绑定     map[string]interface{}
	队列     []func()
	上下文    context.Context
	热键map  map[int]X热键处理程序
	js钩子s  []X接口_JS钩子 // JavaScript hooks

	// 状态听回调
	事件_加载状态改变 func(bool)
	事件_URL改变  func(string)
	事件_标题改变   func(string)
	事件_全屏改变   func(bool)
	事件_关闭窗口   func() bool

	// 用于处理导航的通道
	导航通道 chan string

	// 消息回调
	消息回调 func(string)
}

type X结构_窗口选项 struct {
	X窗口标题   string // 窗口标题
	X窗口类名   string
	X窗口宽度   uint // 窗口宽度
	X窗口高度   uint // 窗口高度
	X是否居中   bool // 是否居中
	X是否无边框  bool // 是否无边框
	X是否全屏   bool // 是否全屏
	X是否置顶   bool // 是否置顶
	X可调整大小  bool // 是否可调整大小
	X最小宽度   int  //只有是否可调整大小为true时才有效
	X最小高度   int  //只有是否可调整大小为true时才有效
	X最大宽度   int  //只有是否可调整大小为true时才有效
	X最大高度   int  //只有是否可调整大小为true时才有效
	X禁止最小化  bool // 是否禁止最小化
	X禁止最大化  bool //  如果设置了"X最小*度/X最大*度" 时,此属性无效
	X初始最小化  bool // 初始是否最小化
	X初始最大化  bool // 初始是否最大化
	X隐藏窗口   bool
	X隐藏任务栏  bool
	X默认背景色  string  // 默认背景色 (CSS 格式，如 "#FFFFFF")
	X初始透明度  float64 // 初始透明度 (0.0-1.0)
	X图标文件路径 string  // 图标文件路径
	X图标字节集  []byte  // 图标二进制数据
}

type X结构_Web视图选项 struct {
	X开发者工具 bool //如果为 true,可以按F12启用调试工具,如果为 false,禁用调试工具
	X右键菜单  bool
	X浏览器缓存 string   //指定 WebView2 运行时使用的数据路径
	X自动聚焦  bool     //窗口打开时，X自动聚焦 将尝试保持 WebView2 小部件聚焦
	X窗口选项  X结构_窗口选项 //X窗口选项 自定义创建的窗口以嵌入
}

// X创建 使用提供的选项创建一个的 Webview。
func X创建(选项 X结构_Web视图选项) *Web视图 {
	if 选项.X窗口选项.X窗口标题 == "" {
		选项.X窗口选项.X窗口标题 = "WebView2"
	}
	if 选项.X窗口选项.X窗口类名 == "" {
		选项.X窗口选项.X窗口类名 = "WebView2"
	}
	if 选项.X窗口选项.X窗口宽度 == 0 {
		选项.X窗口选项.X窗口宽度 = 800
	}
	if 选项.X窗口选项.X窗口高度 == 0 {
		选项.X窗口选项.X窗口高度 = 600
	}
	if 选项.X窗口选项.X默认背景色 == "" {
		选项.X窗口选项.X默认背景色 = "#FFFFFF"
	}
	if 选项.X浏览器缓存 != "" {
		if os.MkdirAll(选项.X浏览器缓存, os.ModePerm) != nil {
			log.Fatal("创建浏览器缓存目录失败")
		}
	}

	w := &Web视图{
		上下文:   context.Background(),
		热键map: make(map[int]X热键处理程序),
	}
	w.绑定 = map[string]interface{}{}
	w.是否自动聚焦 = 选项.X自动聚焦

	浏览器 := edge.NewChromium()
	浏览器.MessageCallback = w.msgcb
	浏览器.DataPath = 选项.X浏览器缓存
	浏览器.Webview2路径 = webview2路径
	浏览器.SetPermission(edge.CoreWebView2PermissionKindClipboardRead, edge.CoreWebView2PermissionStateAllow)

	w.浏览器 = 浏览器
	if !w.创建窗口(选项.X窗口选项) {
		return nil
	}

	settings, err := 浏览器.GetSettings()
	if err != nil {
		log.Printf("警告：获取设置失败: %v", err)
		return nil
	}

	//禁用上下文菜单
	err = settings.PutAreDefaultContextMenusEnabled(选项.X右键菜单)
	if err != nil {
		log.Fatal(err)
	}

	//禁用开者工具
	err = settings.PutAreDevToolsEnabled(选项.X开发者工具)
	if err != nil {
		log.Fatal(err)
	}

	// 设置默认消息处理
	w.X设置消息回调(func(msg string) {
		if chromium, ok := w.浏览器.(*edge.Chromium); ok {
			chromium.HandleWebMessage(msg)
		}
	})

	return w
}

type X结构_RPC消息 struct {
	ID   int               `json:"id"`
	X方法  string            `json:"method"`
	X参数s []json.RawMessage `json:"params"`
}

func (w *Web视图) msgcb(msg string) {
	d := X结构_RPC消息{}
	if err := json.Unmarshal([]byte(msg), &d); err != nil {
		log.Printf("解码RPC消息出错:%v", err)
		return
	}

	id := strconv.Itoa(d.ID)
	rejectScript := fmt.Sprintf("window._rpc[%s].reject", id)
	resolveScript := fmt.Sprintf("window._rpc[%s].resolve", id)
	cleanupScript := fmt.Sprintf("window._rpc[%s] = undefined", id)

	if res, err := w.callbinding(d); err != nil {
		w.X加入队列(func() {
			w.X注入临时js(fmt.Sprintf("%s(%s); %s", rejectScript, jsString(err.Error()), cleanupScript))
		})
	} else if b, err := json.Marshal(res); err != nil {
		w.X加入队列(func() {
			w.X注入临时js(fmt.Sprintf("%s(%s); %s", rejectScript, jsString(err.Error()), cleanupScript))
		})
	} else {
		w.X加入队列(func() {
			w.X注入临时js(fmt.Sprintf("%s(%s); %s", resolveScript, string(b), cleanupScript))
		})
	}
}

func (w *Web视图) callbinding(d X结构_RPC消息) (interface{}, error) {
	w.锁.Lock()
	f, ok := w.绑定[d.X方法]
	w.锁.Unlock()
	if !ok {
		return nil, nil
	}

	v := reflect.ValueOf(f)
	isVariadic := v.Type().IsVariadic()
	numIn := v.Type().NumIn()
	if (isVariadic && len(d.X参数s) < numIn-1) || (!isVariadic && len(d.X参数s) != numIn) {
		return nil, errors.New("函数参数不匹配")
	}
	args := []reflect.Value{}
	for i := range d.X参数s {
		var arg reflect.Value
		if isVariadic && i >= numIn-1 {
			arg = reflect.New(v.Type().In(numIn - 1).Elem())
		} else {
			arg = reflect.New(v.Type().In(i))
		}
		if err := json.Unmarshal(d.X参数s[i], arg.Interface()); err != nil {
			return nil, err
		}
		args = append(args, arg.Elem())
	}

	errorType := reflect.TypeOf((*error)(nil)).Elem()
	res := v.Call(args)
	switch len(res) {
	case 0:
		// 函数没有结果，只返回nil
		return nil, nil

	case 1:
		// 一个结果可能是值，也可能是错误
		if res[0].Type().Implements(errorType) {
			if res[0].Interface() != nil {
				return nil, res[0].Interface().(error)
			}
			return nil, nil
		}
		return res[0].Interface(), nil

	case 2:
		// 两个结果：第一个是值，第二个是错误
		if !res[1].Type().Implements(errorType) {
			return nil, errors.New("第二个返回值必须是错误")
		}
		if res[1].Interface() == nil {
			return res[0].Interface(), nil
		}
		return res[0].Interface(), res[1].Interface().(error)

	default:
		return nil, errors.New("意外的返回值数量")
	}
}

func (w *Web视图) 创建窗口(选项 X结构_窗口选项) bool {
	var 模块句柄 windows.Handle
	if 错误 := windows.GetModuleHandleEx(0, nil, &模块句柄); 错误 != nil {
		log.Printf("获取模块句柄时出错：%v", 错误)
		return false
	}

	图标句柄 := w.取窗口图标句柄(选项.X图标字节集, 选项.X图标文件路径)
	if 图标句柄 == 0 {
		log.Printf("警告：取窗口图标句柄失败")
	}

	窗口类名, _ := windows.UTF16PtrFromString(选项.X窗口类名)
	wc := w32.X结构_窗口类ExW{
		X结构体大小:    uint32(unsafe.Sizeof(w32.X结构_窗口类ExW{})),
		X实例句柄:     模块句柄,
		X类名称:      窗口类名,
		X图标句柄:     windows.Handle(图标句柄),
		X小图标句柄:    windows.Handle(图标句柄),
		X窗口过程函数指针: windows.NewCallback(处理窗口过程),
	}
	_, _, _ = w32.X注册窗口类EXW.Call(uintptr(unsafe.Pointer(&wc)))

	窗口标题, _ := windows.UTF16PtrFromString(选项.X窗口标题)
	窗口宽度 := 选项.X窗口宽度
	窗户高度 := 选项.X窗口高度

	var 坐标X, 坐标Y uint
	if 选项.X是否居中 {
		// 获取屏幕尺寸
		屏幕宽度, _, _ := w32.X获取系统参数.Call(w32.SM_CXSCREEN)
		屏幕高度, _, _ := w32.X获取系统参数.Call(w32.SM_CYSCREEN)
		// 计算窗口位置
		坐标X = (uint(屏幕宽度) - 窗口宽度) / 2
		坐标Y = (uint(屏幕高度) - 窗户高度) / 2
	} else {
		//使用默认位置
		坐标X = w32.CW_USEDEFAULT
		坐标Y = w32.CW_USEDEFAULT
	}

	// 修改窗口样式设置
	var 窗口样式 uint32 = w32.X常量_窗口样式_普通窗口
	if 选项.X是否无边框 {
		窗口样式 = w32.X常量_窗口样式_弹出窗口 | w32.X常量_窗口样式_窗口可见
	} else {
		// 根据选项调整窗样式
		if 选项.X禁止最大化 {
			窗口样式 &^= w32.X常量_窗口样式_最大化按钮
		}
		if 选项.X禁止最小化 {
			窗口样式 &^= w32.X常量_窗口样式_最小化按钮
		}
		if !选项.X可调整大小 {
			窗口样式 &^= w32.X常量_窗口样式_粗边框
		}
	}
	if 选项.X是否全屏 {
		窗口样式 = w32.X常量_窗口样式_弹出窗口 | w32.X常量_窗口样式_窗口可见
		窗口样式 &^= w32.X常量_窗口样式_普通窗口
		var rect w32.X结构_矩形
		_, _, _ = w32.X获取窗口客户区矩形.Call(w.窗口句柄, uintptr(unsafe.Pointer(&rect)))
		全屏宽度, _, _ := w32.X获取系统参数.Call(w32.SM_CXSCREEN)
		全屏高度, _, _ := w32.X获取系统参数.Call(w32.SM_CYSCREEN)
		窗口宽度 = uint(全屏宽度)
		窗户高度 = uint(全屏高度)
		坐标X = 0
		坐标Y = 0
	}

	// 添加分层窗口扩展样式
	var exStyle uint32 = w32.WS_EX_LAYERED
	if 选项.X隐藏任务栏 {
		exStyle |= w32.WS_EX_TOOLWINDOW
	}

	w.窗口句柄, _, _ = w32.X创建窗口EXW.Call(
		uintptr(exStyle),
		uintptr(unsafe.Pointer(窗口类名)),
		uintptr(unsafe.Pointer(窗口标题)),
		uintptr(窗口样式),
		uintptr(坐标X),
		uintptr(坐标Y),
		uintptr(窗口宽度),
		uintptr(窗户高度),
		0,
		0,
		uintptr(模块句柄),
		0,
	)

	// 设置初始透明度(默认完全不透明)
	_, _, _ = w32.X设置分层窗口属性.Call(
		w.窗口句柄,
		0,
		255, // 完全不透明
		w32.LWA_ALPHA,
	)

	设置窗口上下文(w.窗口句柄, w)
	if 选项.X可调整大小 {
		if 选项.X最大宽度 > 0 && 选项.X最大高度 > 0 {
			w.X设置大小(选项.X最大宽度, 选项.X最大高度, X常量_尺寸类型_最大)
		}
		if 选项.X最小宽度 > 0 && 选项.X最小高度 > 0 {
			w.X设置大小(选项.X最小宽度, 选项.X最小高度, X常量_尺寸类型_最小)
		}
	}

	if 选项.X是否置顶 {
		w.X设置置顶(true)
	}
	// 设置初始窗口状态
	if !选项.X禁止最大化 && 选项.X初始最大化 {
		w.X最大化窗口()
	} else if !选项.X禁止最小化 && 选项.X初始最小化 {
		w.X最小化窗口()
	}

	if !选项.X隐藏窗口 {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_显示窗口)
		_, _, _ = w32.X设置前台窗口.Call(w.窗口句柄)
	}
	_, _, _ = w32.X更新窗口.Call(w.窗口句柄)
	_, _, _ = w32.X设置焦点.Call(w.窗口句柄)

	if !w.浏览器.Embed(w.窗口句柄) {
		return false
	}
	w.浏览器.Resize()

	// 设置默认背色
	if 选项.X默认背景色 != "" {
		w.X注入临时js(fmt.Sprintf(`
			document.documentElement.style.background = '%s';
			document.body.style.background = '%s';
		`, 选项.X默认背景色, 选项.X默认背景色))
	}

	return true
}

func (w *Web视图) X销毁() {
	//注所有热键
	w.锁.Lock()
	for id := range w.热键map {
		_, _, _ = w32.X注销热键.Call(w.窗口句柄, uintptr(id))
	}
	w.热键map = nil
	w.锁.Unlock()

	// 清理资源
	w.锁.Lock()
	w.绑定 = nil
	w.队列 = nil
	w.锁.Unlock()

	w.事件_关闭窗口 = nil // 这是手动销毁, 那么就不需要执行这个事件.
	_, _, _ = w32.X发送异步消息W.Call(w.窗口句柄, w32.X常量_关闭, 0, 0)
}
func X运行() {
	var 窗口消息 w32.X窗口消息
	for {
		_, _, _ = w32.X获取消息W.Call(
			uintptr(unsafe.Pointer(&窗口消息)),
			0, // hWnd 参数为 0，表示获取当前线程的所有消息
			0,
			0,
		)

		if w, ok := 取窗口上下文(uintptr(窗口消息.X窗口句柄)).(*Web视图); ok {
			if 窗口消息.X消息类型 == w32.X常量_队列函数 {
				w.锁.Lock()
				q := append([]func(){}, w.队列...)
				w.队列 = []func(){}
				w.锁.Unlock()
				for _, v := range q {
					v()
				}
			}
		}

		if 窗口消息.X消息类型 == w32.X常量_退出 {
			return
		}

		r, _, _ := w32.X获取祖先窗口.Call(uintptr(窗口消息.X窗口句柄), w32.GARoot)
		r, _, _ = w32.X是否为对话框消息.Call(r, uintptr(unsafe.Pointer(&窗口消息)))
		if r != 0 {
			continue
		}
		_, _, _ = w32.X翻译消息.Call(uintptr(unsafe.Pointer(&窗口消息)))
		_, _, _ = w32.X分发消息W.Call(uintptr(unsafe.Pointer(&窗口消息)))
	}
}
func X全部退出() {
	窗口上下文.Range(func(key, value interface{}) bool {
		value.(*Web视图).X销毁()
		return true
	})
}
func (w *Web视图) X显示任务栏(是否显示 ...bool) {
	if len(是否显示) > 0 && 是否显示[0] {
		style := win.GetWindowLong(win.HWND(w.窗口句柄), win.GWL_EXSTYLE)                     // 获取窗口样式
		win.SetWindowLong(win.HWND(w.窗口句柄), win.GWL_EXSTYLE, style&^win.WS_EX_TOOLWINDOW) // 移除 WS_EX_TOOLWINDOW 样式
	} else {
		style := win.GetWindowLong(win.HWND(w.窗口句柄), win.GWL_EXSTYLE)                    // 获取窗口样式
		win.SetWindowLong(win.HWND(w.窗口句柄), win.GWL_EXSTYLE, style|win.WS_EX_TOOLWINDOW) //设置窗口样式
	}
}
func (w *Web视图) X取窗口句柄() int {
	return int(w.窗口句柄)
}
func (w *Web视图) X显示窗口(是否显示 ...bool) {
	if len(是否显示) > 0 && 是否显示[0] {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_显示窗口)
		_, _, _ = w32.X设置前台窗口.Call(w.窗口句柄)
		_, _, _ = w32.X更新窗口.Call(w.窗口句柄)
		_, _, _ = w32.X设置焦点.Call(w.窗口句柄)
	} else {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_隐藏窗口)
	}

}
func (w *Web视图) X导航(链接 string) {
	w.浏览器.Navigate(链接)
}

func (w *Web视图) X设置HTML(html string) {
	w.浏览器.NavigateToString(html)
}

func (w *Web视图) X设置标题(标题 string) {
	w.X加入队列(func() {
		_title, err := windows.UTF16FromString(标题)
		if err != nil {
			_title, _ = windows.UTF16FromString("")
		}
		_, _, _ = w32.X设置窗口文本W.Call(w.窗口句柄, uintptr(unsafe.Pointer(&_title[0])))
	})
}

func (w *Web视图) X设置大小(宽 int, 高 int, 窗口提示 X尺寸类型) {
	w.X加入队列(func() {
		index := w32.GWLStyle
		style, _, _ := w32.X获取窗口长指针W.Call(w.窗口句柄, uintptr(index))
		if 窗口提示 == X常量_尺寸类型_固定 {
			styleUint32 := uint32(style)
			styleUint32 &^= (w32.X常量_窗口样式_粗边框 | w32.X常量_窗口样式_最大化按钮)
			style = uintptr(styleUint32)
		} else {
			styleUint32 := uint32(style)
			styleUint32 |= (w32.X常量_窗口样式_粗边框 | w32.X常量_窗口样式_最大化按钮)
			style = uintptr(styleUint32)
		}
		_, _, _ = w32.X设置窗口长指针W.Call(w.窗口句柄, uintptr(index), style)

		if 窗口提示 == X常量_尺寸类型_最大 {
			w.最大尺寸.X = int32(宽)
			w.最大尺寸.Y = int32(高)
		} else if 窗口提示 == X常量_尺寸类型_最小 {
			w.最小尺寸.X = int32(宽)
			w.最小尺寸.Y = int32(高)
		} else {
			r := w32.X结构_矩形{}
			r.X左 = 0
			r.X上 = 0
			r.X右 = int32(宽)
			r.X下 = int32(高)
			_, _, _ = w32.X调整窗口大小.Call(
				uintptr(unsafe.Pointer(&r)),
				uintptr(w32.X常量_窗口样式_普通窗口),
				0,
			)
			_, _, _ = w32.X设置窗口位置和尺寸.Call(
				w.窗口句柄, 0, uintptr(r.X左), uintptr(r.X上),
				uintptr(r.X右-r.X左), uintptr(r.X下-r.X上),
				w32.SWP_NOZORDER|w32.SWP_NOACTIVATE|w32.SWP_NOMOVE|w32.SWP_FRAMECHANGED)
			w.浏览器.Resize()
		}
	})
}

// 初始化(加载之前注入js，永久注入)
func (w *Web视图) X注入js(脚本 string) {
	// 添加 webview2 导航功能
	baseScript := `
		window.webview2 = {
			navigate: function(url) {
				window.chrome.webview.postMessage(JSON.stringify({
					type: 'navigate',
					url: url
				}));
			}
		};
	`

	// 合并脚本
	fullScript := baseScript
	if 脚本 != "" {
		fullScript += ";" + 脚本
	}

	// 初始化浏览器
	w.浏览器.Init(fullScript)
}

// 执行JS(加载之后注入js，临时注入)
func (w *Web视图) X注入临时js(脚本 string) {
	// 应用前置钩子
	脚本 = w.processScript(脚本, X常量_JS钩子类型_JS执行前)

	w.X加入队列(func() {
		w.浏览器.Eval(脚本)
	})

	// 应用后置钩子
	脚本 = w.processScript(脚本, X常量_JS钩子类型_JS执行后)
}

// X加入队列 异步分发任务
func (w *Web视图) X加入队列(回调函数 func()) {
	go func() {
		w.锁.Lock()
		w.队列 = append(w.队列, 回调函数)
		w.锁.Unlock()
		_, _, _ = w32.X发送异步消息W.Call(w.窗口句柄, w32.X常量_队列函数, 0, 0)
	}()
}
func (w *Web视图) X绑定函数(name string, f interface{}) error {
	v := reflect.ValueOf(f)
	if v.Kind() != reflect.Func {
		return errors.New("只能绑定函数")
	}
	if n := v.Type().NumOut(); n > 2 {
		return errors.New("函数只能返回一个值或一个 值+错误")
	}
	w.锁.Lock()
	w.绑定[name] = f
	w.锁.Unlock()

	w.X注入js("(function() { var name = " + jsString(name) + ";" + `
		var RPC = window._rpc = (window._rpc || {nextSeq: 1});
		window[name] = function() {
		  var seq = RPC.nextSeq++;
		  var promise = new Promise(function(resolve, reject) {
			RPC[seq] = {
			  resolve: resolve,
			  reject: reject,
			};
		  });
		  window.external.invoke(JSON.stringify({
			id: seq,
			method: name,
			params: Array.prototype.slice.call(arguments),
		  }));
		  return promise;
		}
	})()`)

	return nil
}

func (w *Web视图) 取窗口图标句柄(图标字节集 []byte, 图标文件路径 string) uintptr {
	// 1. 优先使用 IconData
	if len(图标字节集) > 0 {
		icon, _, _ := w32.X从资源创建图标.Call(
			uintptr(unsafe.Pointer(&图标字节集[0])),
			uintptr(len(图标字节集)),
			1,          // IMAGE_ICON
			0x00030000, // 版本
			0, 0,
			w32.LR_DEFAULTSIZE,
		)
		if icon != 0 {
			return icon
		}
	}

	// 2. 其次使用 IconPath
	if 图标文件路径 != "" {
		iconPath, _ := windows.UTF16PtrFromString(图标文件路径)
		icon, _, _ := w32.X加载图像W.Call(
			0,
			uintptr(unsafe.Pointer(iconPath)),
			1, // IMAGE_ICON
			0, 0,
			w32.LR_LOADFROMFILE|w32.LR_DEFAULTSIZE,
		)
		if icon != 0 {
			return icon
		}
	}

	// 4. 最后使用默认标
	icow, _, _ := w32.X获取系统参数.Call(w32.SystemMetricsCxIcon)
	icoh, _, _ := w32.X获取系统参数.Call(w32.SystemMetricsCyIcon)
	icon, _, _ := w32.X加载图像W.Call(
		0,
		32512, // IDI_APPLICATION
		1,     // IMAGE_ICON
		icow,
		icoh,
		w32.LR_SHARED,
	)
	return icon
}

// X设置全屏 设置窗口全屏状态
func (w *Web视图) X设置全屏(启用 bool) {
	w.X加入队列(func() {
		if 启用 {
			var rect w32.X结构_矩形
			_, _, _ = w32.X获取窗口客户区矩形.Call(w.窗口句柄, uintptr(unsafe.Pointer(&rect)))

			screenWidth, _, _ := w32.X获取系统参数.Call(w32.SM_CXSCREEN)
			screenHeight, _, _ := w32.X获取系统参数.Call(w32.SM_CYSCREEN)

			style, _, _ := w32.X获取窗口长指针W.Call(w.窗口句柄, uintptr(w32.GWLStyle&0xFFFFFFFF))
			styleUint32 := uint32(style)
			styleUint32 &^= w32.X常量_窗口样式_普通窗口
			style = uintptr(styleUint32)
			_, _, _ = w32.X设置窗口长指针W.Call(w.窗口句柄, uintptr(w32.GWLStyle&0xFFFFFFFF), style)

			_, _, _ = w32.X设置窗口位置和尺寸.Call(
				w.窗口句柄,
				uintptr(w32.HWND_TOP),
				0,
				0,
				screenWidth,
				screenHeight,
				w32.SWP_FRAMECHANGED,
			)
		} else {
			style, _, _ := w32.X获取窗口长指针W.Call(w.窗口句柄, uintptr(w32.GWLStyle&0xFFFFFFFF))
			styleUint32 := uint32(style)

			// 检查是否为无边框窗口
			isFrameless := (styleUint32 & w32.X常量_窗口样式_弹出窗口) != 0

			if !isFrameless {
				styleUint32 |= w32.X常量_窗口样式_普通窗口
			} else {
				styleUint32 = w32.X常量_窗口样式_弹出窗口 | w32.X常量_窗口样式_窗口可见
			}

			style = uintptr(styleUint32)
			_, _, _ = w32.X设置窗口长指针W.Call(w.窗口句柄, uintptr(w32.GWLStyle&0xFFFFFFFF), style)

			// 获取屏幕尺寸
			screenWidth, _, _ := w32.X获取系统参数.Call(w32.SM_CXSCREEN)
			screenHeight, _, _ := w32.X获取系统参数.Call(w32.SM_CYSCREEN)

			// 设置默认窗口大小
			width := uintptr(1024)
			height := uintptr(768)

			// 计算居中置
			x := (screenWidth - width) / 2
			y := (screenHeight - height) / 2

			// 恢复窗口
			_, _, _ = w32.X设置窗口位置和尺寸.Call(
				w.窗口句柄,
				uintptr(w32.HWND_TOP),
				x,
				y,
				width,
				height,
				w32.SWP_FRAMECHANGED,
			)
		}
		w.浏览器.Resize()

		// 触发屏状态改变回调
		if w.事件_全屏改变 != nil {
			w.事件_全屏改变(启用)
		}
	})

}

// X设置置顶 设置窗口置顶状
func (w *Web视图) X设置置顶(启用 bool) {
	w.X加入队列(func() {
		flag := w32.HWND_NOTOPMOST
		if 启用 {
			flag = w32.HWND_TOPMOST
		}
		_, _, _ = w32.X设置窗口位置和尺寸.Call(
			w.窗口句柄,
			uintptr(flag),
			0, 0, 0, 0,
			w32.SWP_NOMOVE|w32.SWP_NOSIZE,
		)
	})
}

// 最小化窗口
func (w *Web视图) X最小化窗口() {
	w.X加入队列(func() {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_最小化窗口)
	})
}

// 最大化窗口
func (w *Web视图) X最大化窗口() {
	w.X加入队列(func() {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_最大化窗口)
	})
}

// 还原窗口
func (w *Web视图) X还原窗口() {
	w.X加入队列(func() {
		_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_还原窗口)
	})
}

// 居中窗口
func (w *Web视图) X居中窗口() {
	w.X加入队列(func() {
		var rect w32.X结构_矩形
		_, _, _ = w32.X获取窗口客户区矩形.Call(w.窗口句柄, uintptr(unsafe.Pointer(&rect)))
		width := int32(rect.X右 - rect.X左)
		height := int32(rect.X下 - rect.X上)

		screenWidth, _, _ := w32.X获取系统参数.Call(w32.SM_CXSCREEN)
		screenHeight, _, _ := w32.X获取系统参数.Call(w32.SM_CYSCREEN)

		x := int32(screenWidth-uintptr(width)) / 2
		y := int32(screenHeight-uintptr(height)) / 2

		_, _, _ = w32.X设置窗口位置和尺寸.Call(
			w.窗口句柄,
			0,
			uintptr(x),
			uintptr(y),
			uintptr(width),
			uintptr(height),
			w32.SWP_NOZORDER|w32.SWP_NOSIZE,
		)
	})
}

// X设置窗口透明度 设置窗口透明度
func (w *Web视图) X设置窗口透明度(透明度 float64) {
	if 透明度 < 0 {
		透明度 = 0
	}
	if 透明度 > 1 {
		透明度 = 1
	}

	w.X加入队列(func() {
		// 确保窗口有分层属性
		style, _, _ := w32.X获取窗口长指针W.Call(w.窗口句柄, ^uintptr(15)) // 使用 ^uintptr(15) 替代 GWL_EXSTYLE
		style |= w32.WS_EX_LAYERED

		_, _, _ = w32.X设置窗口长指针W.Call(w.窗口句柄, ^uintptr(15), style)

		// 设置透明度
		_, _, _ = w32.X设置分层窗口属性.Call(
			w.窗口句柄,
			0,
			uintptr(透明度*255),
			w32.LWA_ALPHA,
		)
	})
}

// 浏览器相关能
func (w *Web视图) X刷新页面() {
	w.X注入临时js("window.location.reload();")
}

func (w *Web视图) X后退() {
	w.X注入临时js("window.history.back();")
}

func (w *Web视图) X前进() {
	w.X注入临时js("window.history.forward();")
}

func (w *Web视图) X停止加载() {
	w.X注入临时js("window.stop();")
}

func (w *Web视图) X关闭开发者工具() {
	w.X注入临时js(`if(window.devtools && window.devtools.isOpen()) window.devtools.close();`)
}

func (w *Web视图) X事件_加载状态变化(回调函数 func(bool)) {
	w.事件_加载状态改变 = 回调函数
}

func (w *Web视图) X事件_URL变化(回调函数 func(string)) {
	w.事件_URL改变 = 回调函数
}

func (w *Web视图) X事件_标题变化(回调函数 func(string)) {
	w.事件_标题改变 = 回调函数
}

func (w *Web视图) X事件_全屏状态变化(回调函数 func(bool)) {
	w.事件_全屏改变 = 回调函数
}

// X事件_关闭窗口 是一个用于设置关闭窗口事件回调函数的方法。
// 它允许在窗口尝试关闭时执行自定义逻辑，以决定是否真正关闭窗口。
// 参数:
// 回调函数 - 一个返回布尔值的函数，用于决定窗口是否关闭。如果返回true，窗口将关闭；否则，窗口保持打开状态。
func (w *Web视图) X事件_关闭窗口(回调函数 func() bool) {
	w.事件_关闭窗口 = 回调函数
}

// X清除缓存 清除浏览器缓存
func (w *Web视图) X清除缓存() {
	// 通过 JavaScript 清除缓存
	w.X注入临时js(`
		if (window.caches) {
			caches.keys().then(function(keyList) {
				return Promise.all(keyList.map(function(key) {
					return caches.delete(key);
				}));
			});
		}
		localStorage.clear();
		sessionStorage.clear();
	`)
}

// X清除cookies 清除浏览器 cookies
func (w *Web视图) X清除cookies() {
	// 通过 JavaScript 清除 cookies
	w.X注入临时js(`
		document.cookie.split(";").forEach(function(c) { 
			document.cookie = c.replace(/^ +/, "")
				.replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/"); 
		});
	`)
}

// X打印 直接使用默认打印机打印前页面
func (w *Web视图) X打印() {
	w.X注入临时js(`window.print()`)
}

// X打印PDF 将当前页面打印为 PDF 文件
func (w *Web视图) X打印PDF(路径 string) {
	// 使用 WebView2 的 PrintToPdf 方法
	if w.浏览器 != nil {
		if err := w.浏览器.PrintToPDF(路径); err != nil {
			log.Printf("无法打印为PDF: %v", err)
		}
	}
}

// X显示打印对话框 显示打印对话框
func (w *Web视图) X显示打印对话框() {
	w.X注入临时js(`window.print()`)
}

// X禁用右键菜单 禁用右键菜单
func (w *Web视图) X禁用右键菜单() error {
	if w.浏览器 != nil {
		return w.浏览器.DisableContextMenu()
	}
	return nil
}

// X启用右键菜单 启用右键菜单
func (w *Web视图) X启用右键菜单() error {
	// 移除 JavaScript 的右键菜单禁用
	w.X注入临时js(`
		document.removeEventListener('contextmenu', function(e) {
			e.preventDefault();
			return false;
		}, true);
	`)

	// 同时恢复 WebView2 的默认上下文菜单
	if w.浏览器 != nil {
		return w.浏览器.EnableContextMenu()
	}
	return nil
}

// X设置最大化 设置窗口最大化状态
func (w *Web视图) X设置最大化(启用 bool) {
	w.X加入队列(func() {
		if 启用 {
			_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_最大化窗口)
		} else {
			_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_还原窗口)
		}
	})
}

// X设置最小化 设置窗口最小化状态
func (w *Web视图) X设置最小化(启用 bool) {
	w.X加入队列(func() {
		if 启用 {
			_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_最小化窗口)
		} else {
			_, _, _ = w32.X显示窗口.Call(w.窗口句柄, w32.X常量_还原窗口)
		}
	})
}

func (w *Web视图) X事件_导航开始(回调函数 func()) {
	if w.浏览器 != nil {
		if chromium, ok := w.浏览器.(*edge.Chromium); ok {
			chromium.NavigationStartingCallback = 回调函数
		}
	}
}

// 设置消息回调
func (w *Web视图) X设置消息回调(回调函数 func(string)) {
	w.消息回调 = 回调函数
	if chromium, ok := w.浏览器.(*edge.Chromium); ok {
		chromium.SetMessageCallback(回调函数)
	}
}

// ------以下是js钩子------
// X添加JS钩子 添加 JavaScript 钩子
func (w *Web视图) X添加JS钩子(钩子 X接口_JS钩子) {
	w.锁.Lock()
	defer w.锁.Unlock()

	// 按优先级插入
	inserted := false
	for i, h := range w.js钩子s {
		if 钩子.X优先级() < h.X优先级() {
			// 在此位置插入
			w.js钩子s = append(w.js钩子s[:i], append([]X接口_JS钩子{钩子}, w.js钩子s[i:]...)...)
			inserted = true
			break
		}
	}
	if !inserted {
		w.js钩子s = append(w.js钩子s, 钩子)
	}
}

// X移除JS钩子 移除 JavaScript 钩子
func (w *Web视图) X移除JS钩子(钩子 X接口_JS钩子) {
	w.锁.Lock()
	defer w.锁.Unlock()

	for i, h := range w.js钩子s {
		if h == 钩子 {
			w.js钩子s = append(w.js钩子s[:i], w.js钩子s[i+1:]...)
			break
		}
	}
}

// X清除所有JS钩子 清除所有 JavaScript 钩子
func (w *Web视图) X清除所有JS钩子() {
	w.锁.Lock()
	defer w.锁.Unlock()
	w.js钩子s = nil
}

// processScript 处理脚本，用所有钩子
func (w *Web视图) processScript(script string, hookType JS钩子类型) string {
	w.锁.Lock()
	defer w.锁.Unlock()

	result := script
	for _, hook := range w.js钩子s {
		if hook.X钩子类型() == hookType {
			result = hook.X处理函数(result)
		}
	}
	return result
}

// ------以下是键盘热键------
func (w *Web视图) X注销热键(文本热键 string) error {
	w.锁.Lock()
	defer w.锁.Unlock()

	修饰符, 键码, err := 解析热键(文本热键)
	if err != nil {
		return fmt.Errorf("无法解析热键: %v", err)
	}
	// 热键生成唯一ID
	hotkeyID := 热键生成唯一ID(修饰符, 键码)

	if hotkeyID > 0 {
		_, _, _ = w32.X注销热键.Call(w.窗口句柄, uintptr(hotkeyID))
		delete(w.热键map, hotkeyID)
	}
	return nil
}

// X注册热键 通过字符串注册热键
// 例如: "Ctrl+Alt+Q"
func (w *Web视图) X注册热键(文本热键 string, 回调函数 X热键处理程序) error {
	w.锁.Lock()
	defer w.锁.Unlock()

	修饰符, 键码, err := 解析热键(文本热键)
	if err != nil {
		return fmt.Errorf("无法解析热键: %v", err)
	}

	// 化键映
	if w.热键map == nil {
		w.热键map = make(map[int]X热键处理程序)
	}

	// 热键生成唯一ID
	hotkeyID := 热键生成唯一ID(修饰符, 键码)

	// 注册热键
	ret, _, err := w32.X注册热键.Call(
		w.窗口句柄,
		uintptr(hotkeyID),
		uintptr(修饰符),
		uintptr(键码),
	)

	if ret == 0 {
		return fmt.Errorf("注册热键失败: %v", err)
	}

	// 保存处理函数
	w.热键map[hotkeyID] = 回调函数
	return nil
}
